package operadores.crossover;

import base.Aleatorio;
import base.ParDeSolucoes;
import base.Solucao;
import base.Vertice;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

// Representa o operador de crossover OX.
public class CrossoverOX implements OperadorCrossover {

    private int pontoDeCorte1;
    private int pontoDeCorte2;

    @Override
    public ParDeSolucoes aplica(ParDeSolucoes pais) {
        Solucao pai1 = pais.getSolucao1();
        Solucao pai2 = pais.getSolucao2();

        int tamanho = pai1.tamanho();
        Vertice[] segmento1 = new Vertice[tamanho];
        Vertice[] segmento2 = new Vertice[tamanho];

        geraPontosDeCorte(tamanho);

        // copia o segmento do meio, do respectivo pai
        for (int i = pontoDeCorte1; i < pontoDeCorte2; i++) {
            segmento1[i] = pai1.getCaminho().get(i);
            segmento2[i] = pai2.getCaminho().get(i);
        }

        List<Vertice> restoPai1 = new ArrayList<>();
        List<Vertice> restoPai2 = new ArrayList<>();
        for (int i = pontoDeCorte2; i < tamanho; i++) {
            restoPai1.add(pai1.getCaminho().get(i));
            restoPai2.add(pai2.getCaminho().get(i));
        }
        for (int i = 0; i < pontoDeCorte2; i++) {
            restoPai1.add(pai1.getCaminho().get(i));
            restoPai2.add(pai2.getCaminho().get(i));
        }

        // remove de restoPaiX os valores do segmento oposto
        for (int i = pontoDeCorte1; i < pontoDeCorte2; i++) {
            restoPai1.remove(segmento2[i]);
            restoPai2.remove(segmento1[i]);
        }

        int j = 0;
        int k = 0;

        for (int i = pontoDeCorte2; i < pai1.tamanho(); i++) {
            segmento1[i] = restoPai2.get(j);
            j++;
            segmento2[i] = restoPai1.get(k);
            k++;
        }

        for (int i = 0; i < pontoDeCorte1; i++) {
            segmento1[i] = restoPai2.get(j);
            j++;
            segmento2[i] = restoPai1.get(k);
            k++;
        }

        Solucao filho1 = new Solucao(new ArrayList<>(Arrays.asList(segmento1)));
        Solucao filho2 = new Solucao(new ArrayList<>(Arrays.asList(segmento2)));
        return new ParDeSolucoes(filho1, filho2);
    }

    // Gera os 2 pontos de corte para o operador OX.
    private void geraPontosDeCorte(int tamanhoDaLista) {
        pontoDeCorte1 = Aleatorio.geraEntre(0, tamanhoDaLista);
        pontoDeCorte2 = Aleatorio.geraEntre(0, tamanhoDaLista);

        while (pontoDeCorte2 == pontoDeCorte1) {
            pontoDeCorte2 = Aleatorio.geraEntre(0, tamanhoDaLista);
        }

        if (pontoDeCorte1 > pontoDeCorte2) {
            int temp = pontoDeCorte1;
            pontoDeCorte1 = pontoDeCorte2;
            pontoDeCorte2 = temp;
        }
    }
}
